﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using VSLangProj;

namespace HydraPackageTool
{
    [Guid("6EE05D8F-AAF9-495e-A8FB-143CD2DC03F5")]
    public class PackageTool : IVsSingleFileGenerator, IObjectWithSite
    {
        private object site;
        private ServiceProvider serviceProvider = null;
        private object dte;
        private string realToolAssembly;
        private IHydraPackageTool realTool;

        public PackageTool()
        {
            var assm = Assembly.GetExecutingAssembly();
            var toolImp = "HydraPackageToolImp.dll";
            var fileInfo = new FileInfo(assm.Location);

            realToolAssembly = Path.Combine(fileInfo.DirectoryName, toolImp);

            var exeFileInfo = new FileInfo(Environment.GetCommandLineArgs().First());
            var destLocation = exeFileInfo.DirectoryName;

            fileInfo.Directory.GetFiles("*.dll").Cast<FileInfo>().ToList().ForEach(f =>
            {
                if (f.Name != toolImp)
                {
                    var destination = Path.Combine(destLocation, f.Name);
                    var destFile = new FileInfo(destination);

                    if (destFile.Exists && f.LastWriteTime > destFile.LastWriteTime)
                    {
                        File.Copy(f.FullName, destination, true);
                    }
                }
            });

            assm = Assembly.LoadFile(realToolAssembly);
            var type = assm.GetType("HydraPackageToolImp.PackageTool");

            realTool = (IHydraPackageTool)Activator.CreateInstance(type);
        }

        private ServiceProvider SiteServiceProvider
        {
            get
            {
                if (serviceProvider == null)
                {
                    Microsoft.VisualStudio.OLE.Interop.IServiceProvider oleServiceProvider = site as Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
                    serviceProvider = new ServiceProvider(oleServiceProvider);
                }

                return serviceProvider;
            }
        }

        public int DefaultExtension(out string pbstrDefaultExtension)
        {
            return realTool.DefaultExtension(out pbstrDefaultExtension);
        }

        public int Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] rgbOutputFileContents, out uint pcbOutput, IVsGeneratorProgress pGenerateProgress)
        {
            return realTool.Generate(wszInputFilePath, bstrInputFileContents, wszInputFilePath, rgbOutputFileContents, out pcbOutput, pGenerateProgress);
        }

        public void GetSite(ref Guid riid, out IntPtr ppvSite)
        {
            if (this.site == null)
            {
                throw new Win32Exception(-2147467259);
            }

            IntPtr objectPointer = Marshal.GetIUnknownForObject(this.site);

            try
            {
                Marshal.QueryInterface(objectPointer, ref riid, out ppvSite);
                if (ppvSite == IntPtr.Zero)
                {
                    throw new Win32Exception(-2147467262);
                }
            }
            finally
            {
                if (objectPointer != IntPtr.Zero)
                {
                    Marshal.Release(objectPointer);
                    objectPointer = IntPtr.Zero;
                }
            }
        }

        public void SetSite(object pUnkSite)
        {
            this.site = pUnkSite;

            realTool.SetSite(pUnkSite);
            realTool.SiteServiceProvider = this.SiteServiceProvider;

            dynamic vsProjectItem = serviceProvider.GetService(typeof(SVsProjectItem).GUID);

            this.dte = vsProjectItem.DTE;
            realTool.DTE = vsProjectItem.DTE;
        }
    }
}
